//Copyright (c) Microsoft Corporation. All rights reserved.
using System;
using System.Diagnostics;
using System.Xml;

namespace Microsoft.Office.OneNote
{
	/// <summary>
	/// A representation of a generic Folder in OneNote.  NotebookRoot, 
	/// Notebook, UnfiledNotes, and SectionGroup all extend this class 
	/// for their implementations.
	/// </summary>
	public abstract class Folder : HierarchyNode
	{
		/// <summary>
		/// Creates a new folder.
		/// </summary>
		public Folder()
		{
		}

		/// <summary>
		/// Folders are considered to be equal if they have the same Id.
		/// </summary>
		/// <param name="obj">Object to compare to.</param>
		/// <returns></returns>
		public override bool Equals(object obj)
		{
			Folder folder = obj as Folder;
			if (folder == null)
				return false;

			return folder.Id.Equals(Id);
		}

		/// <summary>
		/// Computes a hash based off of the unique folder id.
		/// </summary>
		/// <returns>A hash code for the current <see cref="Folder"/>.</returns>
		public override int GetHashCode()
		{
			return Id.GetHashCode();
		}

		/// <summary>
		/// Useful for debugging.
		/// </summary>
		/// <returns>A string representation of the current folder.</returns>
		public override string ToString()
		{
			return "Folder: " + this.Path;
		}

		/// <summary>
		/// Tests to see if this folder can contain the specified child node.
		/// </summary>
		/// <param name="child">The child which we are testing.</param>
		/// <returns>Returns true if the child node is of type folder or section.</returns>
		public override bool CanContain(ANode child)
		{
			return (child is SectionGroup || child is Section);
		}

		/// <summary>
		/// Serializes the <see cref="Folder"/> object to Xml in a format
		/// suitable for import into OneNote.
		/// </summary>
		/// <param name="parentNode">
		/// A reference to the parent XML node that we should serialize ourselves
		/// as a child of.
		/// </param>
		/// <param name="deep">
		/// Should we recursively serialize our children?
		/// </param>
		/// <returns>
		/// The serialized node.
		/// </returns>
		protected internal override XmlNode SerializeToXml(XmlNode parentNode, bool deep)
		{
			XmlDocument document = parentNode as XmlDocument;
			if (document == null)
				document = parentNode.OwnerDocument;

			XmlElement serialized = document.CreateElement(SerializedXmlType, OneNote.ApplicationServer.XmlNamespace);
			SerializeFolderProperties(serialized);

			parentNode.AppendChild(serialized);

			if (deep)
			{
				foreach (ANode child in children)
				{
					child.SerializeToXml(serialized, deep);
				}
			}

			return serialized;
		}

		/// <summary>
		/// Helper method used while serializing to allow every folder sub-class to serialize
		/// it's properties...
		/// </summary>
		/// <param name="serializedNode">Our serialized XmlElement</param>
		protected internal virtual void SerializeFolderProperties(XmlElement serializedElement)
		{
			serializedElement.SetAttribute("ID", this.Id.ToString());

			serializedElement.SetAttribute("name", this.Name);
			serializedElement.SetAttribute("path", this.Path);
		}

		/// <summary>
		/// Deserializes the <see cref="Folder"/> object from OneNote's xml 
		/// representation.
		/// </summary>
		/// <param name="node">
		/// A reference to the XML node that we should deserialize ourselves
		/// from.
		/// </param>
		protected internal override void DeserializeFromXml(XmlNode node)
		{
			XmlElement element = (XmlElement) node;

			// Set properties:
			this.Path = element.GetAttribute("path");
			this.Name = element.GetAttribute("name");

			// Process children:
			if (HierarchyXmlHelper.NodeChildrenAreLoaded(element))
			{
				foreach (XmlNode child in element.ChildNodes)
				{
					XmlElement childElement = child as XmlElement;
					if (childElement == null)
						continue;

					HierarchyNode deserialized = null;
					switch (childElement.LocalName)
					{
						case "Notebook":
							deserialized = new Notebook();
							break;

						case "UnfiledNotes":
							deserialized = new UnfiledNotes();
							break;

						case "SectionGroup":
							deserialized = new SectionGroup();
							break;

						case "Section":
							deserialized = new Section();
							break;

						default:
							// Unrecognized element child
							Debug.Assert(false);
							break;
					}

					if (deserialized != null)
					{
						deserialized.DeserializeFromXml(childElement);
						this.AppendChild(deserialized);
					}
				}
			}

			base.DeserializeFromXml(node);
		}

		/// <summary>
		/// The name of this folder.
		/// </summary>
		public string Name
		{
			get { return GetProperty_Else("name", ""); }
			set { SetProperty("name", value); }
		}

		/// <summary>
		/// The path in the file system that this folder represents.
		/// </summary>
		public string Path
		{
			get { return GetProperty_Else("path", ""); }
			set { SetProperty("path", value); }
		}

		/// <summary>
		/// The serialized Xml element type 
		/// </summary>
		protected abstract internal string SerializedXmlType
		{
			get;
		}
	}
}
